src/http.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. http_html_prelude
  2. rfc1122_date
  3. small_date
  4. http_file_prelude
  5. http_emit_header
  6. http_emit_footer
  7. http_bad_url
  8. url_get_file
  9. render_idx
  10. html_news
  11. html_td
  12. html_td_news
  13. html_tdb
  14. html_tdl
  15. add_i
  16. html_tdi
  17. add_f
  18. html_tdf
  19. html_tdpercent
  20. html_tdd
  21. html_tdt
  22. html_newsgroup
  23. MAC
  24. add
  25. MAC_BIG
  26. MAC_STR
  27. MAC_LEN
  28. MAC_TIM
  29. CPU2BIG
  30. MAC
  31. MAC
  32. MAC
  33. MAC
  34. MAC
  35. MAC_BIG
  36. MAC
  37. MAC
  38. server_downtime
  39. server_uptime
  40. MAC
  41. MAC
  42. MAC
  43. MAC
  44. MAC
  45. MAC
  46. MAC
  47. S
  48. macro_call
  49. macro_parse
  50. http_macro
  51. url_rewrite
  52. http_get
  53. http_cmd
  54. httpHandler

/* $Id: http.c,v 1.2 1998/08/02 20:35:00 proff Exp $
 * $Copyright$
 */

#include "nglobal.h"
#include "acc.h"
#include "group.h"
#include "ll.h"
#include "list.h"

#include "confused_runtime.h"

#include "http.h"

#define HTTP "HTTP/1.0"
#define TABLE "<table border=2>\n"
#define ENDTABLE "</table>\n"

struct macro_func
{
    char *name;
    void (*func)(struct strStack *out, int argc, char **argv);
    int min_args;
};

static void http_html_prelude (char *msg)
/* [<][>][^][v][top][bottom][index][help] */
{
        emitf("\
%s %s\r\n\
Server: NNTPCache %s\r\n\
Connection: close\r\n\
Content-Type: text/html\r\n\r\n", HTTP, msg, VERSION);
}

EXPORT char *rfc1122_date (time_t ti)
/* [<][>][^][v][top][bottom][index][help] */
{
    static char buf[80];
    struct tm *tm;
    tm = gmtime(&ti);
    if (!tm)
        {
            loge (("gmtime() failed"));
            return "time error"; /* XXX */
        }
    
    strftime (buf, sizeof buf, "%d %b %Y %H:%M:%S %Z", tm);
    return buf;
}

static char *small_date (time_t ti)
/* [<][>][^][v][top][bottom][index][help] */
{
    static char buf[80];
    struct tm *tm;
    tm = localtime(&ti);
    if (!tm)
        {
            loge (("localtime() failed"));
            return "time error"; /* XXX */
        }
    strftime (buf, sizeof buf, (time(NULL)-ti > 3600*24*180)? "%d %b %y %H:%M:%S" : "%d %b %H:%M:%S", tm);
    return buf;
}

static void http_file_prelude (char *url, int len, time_t modified)
/* [<][>][^][v][top][bottom][index][help] */
{
    char *p;
    char *content = "text/plain";
    char *pragma;
    p = strrchr (url, '.');
    if (p)
        {
            p++;
            if (strCaseEq (p, "html") || strCaseEq (p, "htm"))
                content = "text/html";
            else
            if (strCaseEq (p, "gif"))
                content = "image/gif";
            else
            if (strCaseEq (p, "jpg") || strCaseEq (p, "jpeg"))
                content = "image/jpeg";
        }
    if (modified == 0)
        {
            modified = time(NULL);
            pragma = "Pragma: NoCache\r\n";
        }
    else
        {
            pragma = "";
        }
    emitf("\
%s 200 ok\r\n\
Server: NNTPCache %s\r\n\
Content-Type: %s\r\n\
Content-Length: %d\r\n\
Connection: close\r\n\
%s", HTTP, VERSION, content, len, pragma);
    emitf("Last-Modified: %s\r\n", rfc1122_date(modified));
    emitf("Date: %s\r\n\r\n", rfc1122_date(time(NULL)));
}

static void http_emit_header (char *status, char *title)
/* [<][>][^][v][top][bottom][index][help] */
{
    http_html_prelude (status);
    emitf ("\
<HTML>\n\
<HEAD>\n\
<TITLE>%s</TITLE>\n\
</HEAD>\n\
<BODY>\n", title);
}

static void http_emit_footer ()
/* [<][>][^][v][top][bottom][index][help] */
{
    emitf ("\
</BODY>\n\
</HTML>\n");
}

static void http_bad_url (char *url)
/* [<][>][^][v][top][bottom][index][help] */
{
    http_emit_header (HTTP_STATUS_NOTFOUND, "File not found");
    emitf ("<H1>%s</H1>\nThe requested URL %s was not found on this server\n", "File not found", url);
    http_emit_footer ();
}

static char *url_get_file (char *url, int *len, char **outfn, time_t *modified)
/* [<][>][^][v][top][bottom][index][help] */
{
    struct stat st;
    int fd = -1;
    char *p;
    char *fn;
    if (strnEq (url, "../", 3) || strstr (url, "/.."))
        {
            logwn (("hack attempt -- URL contains \"/..\" or \"../\": '%.128s'", url));
            return NULL;
        }
    for (fn = url; *fn && *fn == '/'; fn++) {}
    if (fn[0] == '\0')
        fn = "index.html";
    *outfn = fn;
    if (stat (fn, &st) != 0 || st.st_size < 1)
        return NULL;
    if (st.st_mode & S_IFDIR)
        {
            static char path[MAX_PATH]; /* note static */
            snprintf(path, sizeof path, "%s/index.html", fn);
            *outfn = fn = path;
            if (stat (fn, &st) != 0 || st.st_size < 1)
                return NULL;
        }
    *outfn = fn;
    fd = open(fn, O_RDONLY);
    if (fd<0)
        return NULL;
    p = Smalloc (st.st_size);
    if (read (fd, p, st.st_size) != st.st_size)
        {
            loge (("read ('%s') failed", fn));
            free (p);
            close (fd);
            return NULL;
        }
    *len = st.st_size;
    *modified = st.st_mtime;
    return p;
}

static char *render_idx(struct confused_idx *idx, char **t)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf[MAX_LINE] = "unknown";
    struct strList *sl;
    char *p;
    int l;
    char *ret = buf;
    switch (idx->type)
        {
        case cf_string:
            *t = "string";
            ret = *(char**)idx->data;
            break;
        case cf_stringl:
            *t = "list";
            for (p=buf, sl = *(struct strList**)idx->data; sl; sl=sl->next)
                {
                    l = strlen(sl->data);
                    if (!l)
                        continue;
                    if (p != buf)
                        {
                            memcpy(p, ", ", 3);
                            p += 2;
                        }
                    memcpy(p, sl->data, l+1);
                    p += l;
                }
            break;
        case cf_bool:
            *t = "bool";
            ret = (*(bool*)idx->data)? "true": "false";
            break;
        case cf_int:    
            *t = "int";
            sprintf(buf, "%d", *(int*)idx->data);
            break;
        case cf_time:   
            *t = "time";
            ret = nnitod(*(long*)idx->data);
            break;
        default:
            *t = "unknown";
            break;
        }
    return ret;
}
    
static void html_news(struct strStack *out, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
    strStackAdd(out, "<a href=\"news:");
    strStackAdd(out, s);
    strStackAdd(out, "\">");
    strStackAdd(out, s);
    strStackAdd(out, "</a>");
}

static void html_td(struct strStack *out, char *s, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    strStackAdd(out, "<td align=");
    strStackAdd(out, align);
    strStackAdd(out, ">");
    if (s && s[0])
        strStackAdd(out, s);
    strStackAdd(out, "</td>");
}

static void html_td_news(struct strStack *out, char *s, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    strStackAdd(out, "<td align=");
    strStackAdd(out, align);
    strStackAdd(out, ">");
    if (s)
        html_news(out, s);
    strStackAdd(out, "</td>");
}

static void html_tdb(struct strStack *out, big_t big, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    if (big == 0)
        html_td(out, NULL, align);
    else
        html_td(out, bigToStr(big), align);
}

static void html_tdl(struct strStack *out, big_t big, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    if (big == 0)
        html_td(out, NULL, align);
    else
        html_td(out, (conv(big)[0] == '0')? "": conv(big), align);
}

static void add_i(struct strStack *out, int i)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf [128];
    sprintf(buf, "%d", i);
    strStackAdd(out, buf);
}

static void html_tdi(struct strStack *out, int i, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf [32];
    if (i == 0) 
        {
        html_td(out, NULL, align);
        return;
        }
    sprintf(buf, "%d", i);
    html_td(out, buf, align);
}

static void add_f(struct strStack *out, float f)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf [128];
    sprintf(buf, "%.02f", f);
    strStackAdd(out, buf);
}

static void html_tdf(struct strStack *out, float f, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf [128];
    if (f == 0.0)
        {
        html_td(out, NULL, align);
        return;
        }
    sprintf(buf, "%.02f", f);
    html_td(out, buf, align);
}

static void html_tdpercent(struct strStack *out, float f, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf [128];
    if (f == 0.0)
        {
        html_td(out, NULL, align);
        return;
        }
    sprintf(buf, "%.2f%%", f*100.0);
    html_td(out, buf, align);
}

static void html_tdd(struct strStack *out, time_t ti, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    if (ti == 0)
        {
        html_td(out, NULL, align);
        return;
        }
    html_td(out, small_date(ti), align);
}

static void html_tdt(struct strStack *out, long i, char *align)
/* [<][>][^][v][top][bottom][index][help] */
{
    if (i == 0)
        html_td(out, NULL, align);
    else
        html_td(out, nnitod(i), align);
}

/* argv[0] == first newsgroup */

static void html_newsgroup(struct strStack *out, int argc, char **argv, struct newsgroup *ng)
/* [<][>][^][v][top][bottom][index][help] */
{
    int n;
    strStackAdd(out, "<tr>");
    for (n=0; n<argc; n++)
        {
            char *s = argv[n];
            char *p = strchr(s, '-');
            if (p)
                *p = '\0';
            if (strCaseEq(s, "Group")) html_td_news(out, ng->group, "left"); else
            if (strCaseEq(s, "Messages")) html_tdi(out, ng->msgs, "right"); else
            if (strCaseEq(s, "Lo")) html_tdi(out, ng->lo, "right"); else
            if (strCaseEq(s, "LoServer")) html_tdi(out, ng->lo_server, "right"); else
            if (strCaseEq(s, "Hi")) html_tdi(out, ng->hi, "right"); else
            if (strCaseEq(s, "HiServer")) html_tdi(out, ng->hi_server, "right"); else
            if (strCaseEq(s, "LoXover")) html_tdi(out, ng->lo_xover, "right"); else
            if (strCaseEq(s, "HiXover")) html_tdi(out, ng->hi_xover, "right"); else
            if (strCaseEq(s, "Mod")) {char b[2]="y"; b[0]=ng->moderation; html_td(out, b, "center");} else
            if (strCaseEq(s, "Creator")) html_td(out, ng->creator, "center"); else
            if (strCaseEq(s, "Creation")) html_tdd(out, ng->creation_time, "right"); else
            if (strCaseEq(s, "Rebuild")) html_tdd(out, ng->last_rebuild, "right"); else
            if (strCaseEq(s, "GroupTime")) html_tdd(out, ng->group_time, "right"); else
            if (strCaseEq(s, "GroupChange")) html_tdd(out, ng->group_change_time, "right"); else
            if (strCaseEq(s, "ListGroup")) html_tdd(out, ng->listgroup_time, "right"); else
            if (strCaseEq(s, "Description")) html_td(out, ng->desc, "left"); else
            if (strCaseEq(s, "Server")) {struct server_cfg *scfg = getServerGroup(ng->group); html_td(out, scfg? scfg->host: NULL, "right");} else
            {logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
            if (p)
                *p = '-';
        }
    strStackAdd(out, "</tr>\n");
};

#define MAC(x) \
/* [<][>][^][v][top][bottom][index][help] */
static void macro_ ## x (struct strStack *out, int argc, char **argv)
#define add(x) (strStackAdd(out, (x)))
/* [<][>][^][v][top][bottom][index][help] */
#define MAC_BIG(x) MAC(x) {if (Stats->x) add(bigToStr(Stats->x));}
/* [<][>][^][v][top][bottom][index][help] */
#define MAC_STR(x) MAC(x) {add(x);}
/* [<][>][^][v][top][bottom][index][help] */
#define MAC_LEN(x) MAC(x) {add((conv(Stats->x)[0] == '0')? "": conv(Stats->x));}
/* [<][>][^][v][top][bottom][index][help] */
#define MAC_TIM(x) MAC(x) {add(small_date((Stats->x)));}
/* [<][>][^][v][top][bottom][index][help] */
#define CPU2BIG(x) ((x)/(CLK_TCK))
/* [<][>][^][v][top][bottom][index][help] */
MAC(version)    {add(VERSION);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(hostname)   {add(Host);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(date)       {add(small_date(time(NULL)));}
/* [<][>][^][v][top][bottom][index][help] */
MAC(clienthost) {add(ClientHost);}
/* [<][>][^][v][top][bottom][index][help] */
MAC(efficiency) {add_f(out, (1.0-((float)(Stats->serverFromBytes+Stats->serverToBytes+1)/(float)(Stats->clientToBytes+Stats->clientFromBytes+1)))*100.0);}
/* [<][>][^][v][top][bottom][index][help] */

MAC_BIG(IPCfromChild)
/* [<][>][^][v][top][bottom][index][help] */
MAC_LEN(IPCfromChildBytes)
MAC_BIG(IPCtoChild)
MAC_LEN(IPCtoChildBytes)
MAC_BIG(articlesExpired)
MAC_BIG(clientConnects)
MAC_BIG(clientConnectsFailed)
MAC_LEN(clientFromBytes)
MAC_LEN(clientToBytes)
MAC_BIG(clientsActive)
MAC_BIG(crossposts)
MAC_LEN(crosspostsBytes)
MAC_BIG(groupsCached)
MAC_BIG(groupsExpired)
MAC_BIG(xoversExpired)
MAC_BIG(historyFetches)
MAC_LEN(historySize)
MAC_BIG(historyStores)
MAC_TIM(masterStarted)
MAC_BIG(posts)
MAC_BIG(postsCross)
MAC_LEN(postsBytes)
MAC_BIG(postsFailed)
MAC_BIG(serverConnects)
MAC_BIG(serverConnectsFailed)
MAC_LEN(serverFromBytes)
MAC_LEN(serverToBytes)
MAC_BIG(invocations)
MAC_TIM(statsStarted)

MAC(conftable)
{
    struct confused_idx *idx;
    strStackAdd(out, TABLE);
    strStackAdd(out, "<tr><th align=left>Type</th><th align=left>Name</th><th align=left>Value</th></tr>\n"); 
    for (idx = nnconf_idx; idx->name; idx++)
        {
            char *type;
            char *val;
            strStackAdd(out, "<tr>");
            val = render_idx (idx, &type);
            html_td(out, type, "left");
            html_td(out, idx->name, "left");
            html_td(out, val, "left");
            strStackAdd(out, "</tr>\n");
        }
    strStackAdd(out, ENDTABLE);
}

MAC(html_th)
/* [<][>][^][v][top][bottom][index][help] */
{
    int n;
    strStackAdd(out, "<tr>");
    for (n=0; n<argc; n++)
        {
            char *p;
            strStackAdd(out, "<th align=center>");
            p = strchr(argv[n], '-');
            strStackAdd(out, p? p+1: argv[n]);
            strStackAdd(out, "</th>");
        }
    strStackAdd(out, "</tr>\n");
}

MAC(html_table)
/* [<][>][^][v][top][bottom][index][help] */
{
    strStackAdd(out, TABLE);
    macro_html_th(out, argc, argv);
}

static int server_downtime(struct server_cfg *p)
/* [<][>][^][v][top][bottom][index][help] */
{
    
    if (p->share->server_down > p->share->server_up)
        return p->share->server_down_time + (time(NULL) - p->share->server_down);
    else
        return p->share->server_down_time;
}

static int server_uptime(struct server_cfg *p)
/* [<][>][^][v][top][bottom][index][help] */
{
    return p->share->server_up_time + ((p->share->server_up > p->share->server_down)? time(NULL) - p->share->server_up: 0);
}

MAC(servers)
/* [<][>][^][v][top][bottom][index][help] */
{
    struct server_cfg *p;
    macro_html_table(out, argc-1, argv+1);
    for (p = ServerList; p; p = p->next)
        {
            int n;
            strStackAdd(out, "<tr>");
            for (n=1; n<argc; n++)
                {
                    char *s = argv[n];
                    char *s2 = strchr(s, '-');
                    if (s2)
                        *s2 = '\0';
                    if (strCaseEq(s, "ActiveBytes")) html_tdl(out, p->share->list[l_active].bytes, "right"); else
                    if (strCaseEq(s, "ActiveEntries")) html_tdb(out, p->share->list[l_active].entries, "right"); else
                    if (strCaseEq(s, "ActiveLines")) html_tdb(out, p->share->list[l_active].lines, "right"); else
                    if (strCaseEq(s, "ActiveRebuildFail")) html_tdd(out, p->share->list[l_active].rebuild_fail, "right"); else
                    if (strCaseEq(s, "ActiveRebuildGood")) html_tdd(out, p->share->list[l_active].rebuild_good, "right"); else
                    if (strCaseEq(s, "ActiveRebuildRefused")) html_tdd(out, p->share->list[l_active].rebuild_refused, "right"); else
                    if (strCaseEq(s, "ActiveTimesBytes")) html_tdl(out, p->share->list[l_active_times].bytes, "right"); else 
                    if (strCaseEq(s, "ActiveTimesEntries")) html_tdb(out, p->share->list[l_active_times].entries, "right");  else
                    if (strCaseEq(s, "ActiveTimesLines")) html_tdb(out, p->share->list[l_active_times].lines, "right"); else
                    if (strCaseEq(s, "ActiveTimesRebuildFail")) html_tdd(out, p->share->list[l_active_times].rebuild_fail, "right"); else
                    if (strCaseEq(s, "ActiveTimesRebuildGood")) html_tdd(out, p->share->list[l_active_times].rebuild_good, "right"); else
                    if (strCaseEq(s, "ActiveTimesRebuildRefused")) html_tdd(out, p->share->list[l_active_times].rebuild_refused, "right"); else
                    if (strCaseEq(s, "Host")) html_td(out, p->host, "left"); else
                    if (strCaseEq(s, "NewsgroupsBytes")) html_tdl(out, p->share->list[l_newsgroups].bytes, "right"); else
                    if (strCaseEq(s, "NewsgroupsEntries")) html_tdb(out, p->share->list[l_newsgroups].entries, "right"); else
                    if (strCaseEq(s, "NewsgroupsRebuildGood")) html_tdd(out, p->share->list[l_newsgroups].rebuild_good, "right"); else
                    if (strCaseEq(s, "NewsgroupsLines")) html_tdb(out, p->share->list[l_newsgroups].lines, "right"); else
                    if (strCaseEq(s, "NewsgroupsRebuildFail")) html_tdd(out, p->share->list[l_newsgroups].rebuild_fail, "right"); else
                    if (strCaseEq(s, "NewsgroupsRebuildRefused")) html_tdd(out, p->share->list[l_newsgroups].rebuild_refused, "right"); else
                    if (strCaseEq(s, "OverviewFmtBytes")) html_tdl(out, p->share->list[l_overview_fmt].bytes, "right"); else
                    if (strCaseEq(s, "OverviewFmtEntries")) html_tdb(out, p->share->list[l_overview_fmt].entries, "right"); else
                    if (strCaseEq(s, "OverviewFmtLines")) html_tdb(out, p->share->list[l_overview_fmt].lines, "right"); else
                    if (strCaseEq(s, "OverviewFmtRebuildFail")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_fail, "right"); else
                    if (strCaseEq(s, "OverviewFmtRebuildGood")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_good, "right"); else
                    if (strCaseEq(s, "OverviewFmtRebuildRefused")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_refused, "right"); else
                    if (strCaseEq(s, "ArticleFail")) html_tdb(out, p->share->article_fail, "right"); else
                    if (strCaseEq(s, "ArticleGood")) html_tdb(out, p->share->article_good, "right"); else
                    if (strCaseEq(s, "BodyFail")) html_tdb(out, p->share->body_fail, "right"); else
                    if (strCaseEq(s, "BodyGood")) html_tdb(out, p->share->body_good, "right"); else
                    if (strCaseEq(s, "BytesTo")) html_tdl(out, p->share->bytes_to, "right"); else
                    if (strCaseEq(s, "ConnectFail")) html_tdb(out, p->share->connect_fail, "right"); else
                    if (strCaseEq(s, "GroupFail")) html_tdb(out, p->share->group_fail, "right"); else
                    if (strCaseEq(s, "GroupGood")) html_tdb(out, p->share->group_good, "right"); else
                    if (strCaseEq(s, "HeadFail")) html_tdb(out, p->share->head_fail, "right"); else
                    if (strCaseEq(s, "HeadGood")) html_tdb(out, p->share->head_good, "right"); /* mmm. good head */ else
                    if (strCaseEq(s, "IhaveFail")) html_tdb(out, p->share->ihave_fail, "right"); else
                    if (strCaseEq(s, "IhaveGood")) html_tdb(out, p->share->ihave_good, "right"); else
                    if (strCaseEq(s, "ListgroupFail")) html_tdb(out, p->share->listgroup_fail, "right"); else
                    if (strCaseEq(s, "ListgroupGood")) html_tdb(out, p->share->listgroup_good, "right"); else
                    if (strCaseEq(s, "NewnewsFail")) html_tdb(out, p->share->newnews_fail, "right"); else
                    if (strCaseEq(s, "NewnewsGood")) html_tdb(out, p->share->newnews_good, "right"); else
                    if (strCaseEq(s, "NewsgroupsRebuild")) html_tdd(out, p->share->list[l_newsgroups].rebuild_good, "right"); else
                    if (strCaseEq(s, "OverviewFmtRebuild")) html_tdd(out, p->share->list[l_overview_fmt].rebuild_good, "right"); else
                    if (strCaseEq(s, "PostFail")) html_tdb(out, p->share->post_fail, "right"); else
                    if (strCaseEq(s, "PostGood")) html_tdb(out, p->share->post_good, "right"); else
                    if (strCaseEq(s, "RelayFail")) html_tdb(out, p->share->relay_fail, "right"); else
                    if (strCaseEq(s, "RelayGood")) html_tdb(out, p->share->relay_good, "right"); else
                    if (strCaseEq(s, "ServerDown")) html_tdd(out, p->share->server_down, "right"); else
                    if (strCaseEq(s, "ServerUp")) html_tdd(out, p->share->server_up, "right"); else
                    if (strCaseEq(s, "StatFail")) html_tdb(out, p->share->stat_fail, "right"); else
                    if (strCaseEq(s, "StatGood")) html_tdb(out, p->share->stat_good, "right"); else
                    if (strCaseEq(s, "Us")) html_td(out, p->us, "left"); else
                    if (strCaseEq(s, "XhdrFail")) html_tdb(out, p->share->xhdr_fail, "right"); else
                    if (strCaseEq(s, "XhdrGood")) html_tdb(out, p->share->xhdr_good, "right"); else
                    if (strCaseEq(s, "XoverFail")) html_tdb(out, p->share->xover_fail, "right"); else
                    if (strCaseEq(s, "XoverGood")) html_tdb(out, p->share->xover_good, "right"); else
                    if (strCaseEq(s, "ConnectGood")) html_tdb(out, p->share->connect_good, "right"); else
                    if (strCaseEq(s, "Availability")) {float f = (float)server_downtime(p)+(float)server_uptime(p); html_tdpercent(out, (f>0.0)? (float)server_uptime(p)/f: 0.0, "right");} else
                    if (strCaseEq(s, "BytesFrom")) html_tdl(out, p->share->bytes_from, "right"); else
                    if (strCaseEq(s, "DownTime")) html_tdt(out, server_downtime(p), "right"); else
                    if (strCaseEq(s, "UpTime")) html_tdt(out, server_uptime(p), "right"); else
                    {logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
                    if (s2)
                        *s2 = '-';
                }
            strStackAdd(out, "</tr>\n");
        }
    strStackAdd(out, ENDTABLE);
}

MAC(cacheStats)
/* [<][>][^][v][top][bottom][index][help] */
{
    int m;
    macro_html_table(out, argc-1, argv+1);
    for (m = 0; m<c_none; m++)
        {
            int n;
            struct cache_stats *p = &Stats->cache_stats[m];
            strStackAdd(out, "<tr>");
            for (n=1; n<argc; n++)
                {
                    char *s = argv[n];
                    char *s2 = strchr(s, '-');
                    if (s2)
                        *s2 = '\0';
                    if (strCaseEq(s, "Type")) {struct command *c=commands;for(;c->cmd;c++) if (c->val == m) {html_td(out, c->cmd, "left");break;}} else
                    if (strCaseEq(s, "Requests")) html_tdb(out, p->requests, "right"); else
                    if (strCaseEq(s, "RequestsGood")) html_tdb(out, p->requests_good, "right"); else
                    if (strCaseEq(s, "RequestsFailed")) html_tdb(out, p->requests_failed, "right"); else
                    if (strCaseEq(s, "FilterBlocked")) html_tdb(out, p->filter_blocked, "right"); else
                    if (strCaseEq(s, "AuthBlocked")) html_tdb(out, p->auth_blocked, "right"); else
                    if (strCaseEq(s, "NocemBlocked")) html_tdb(out, p->nocem_blocked, "right"); else
                    if (strCaseEq(s, "ServerFromBytes")) html_tdl(out, p->serverFromBytes, "right"); else
                    if (strCaseEq(s, "ClientToBytes")) html_tdl(out, p->clientToBytes, "right"); else
                    if (strCaseEq(s, "ServerToBytes")) html_tdl(out, p->serverToBytes, "right"); else
                    if (strCaseEq(s, "ClientFromBytes")) html_tdl(out, p->clientFromBytes, "right"); else
                    if (strCaseEq(s, "ByMsgid")) html_tdb(out, p->by_msgid, "right"); else
                    if (strCaseEq(s, "ByArtnum")) html_tdb(out, p->by_artnum, "right"); else
                    {logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); add("<td></td>");}
                    if (s2)
                        *s2 = '-';
                }
            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(nocem)
/* [<][>][^][v][top][bottom][index][help] */
{
    int n;
    struct strList *sl;
    macro_html_table(out, argc-1, argv+1);
    for (n=0, sl = con->nocemGroups; sl && n<MAX_NOCEM; sl=sl->next, n++)
        {
            struct nocem_stats *p = &Stats->nocem_stats[n];
            strStackAdd(out, "<tr>");
            for (n=1; n<argc; n++)
                {
                    char *s = argv[n];
                    char *s2 = strchr(s, '-');
                    if (s2)
                        *s2 = '\0';
                    if (strCaseEq(s, "Group")) html_td(out, sl->data, "left"); else
                    if (strCaseEq(s, "ArtHi")) html_tdi(out, p->art_hi, "right"); else
                    if (strCaseEq(s, "ArtGood")) html_tdb(out, p->art_good, "right"); else
                    if (strCaseEq(s, "ArtFail")) html_tdb(out, p->art_fail, "right"); else
                    if (strCaseEq(s, "MsgidGood")) html_tdb(out, p->msgid_good, "right"); else
                    if (strCaseEq(s, "MsgidDup")) html_tdb(out, p->msgid_dup, "right"); else
                    if (strCaseEq(s, "MsgidFail")) html_tdb(out, p->msgid_fail, "right"); else
                    if (strCaseEq(s, "LastScan")) html_tdd(out, p->last_scan, "right"); else
                    if (strCaseEq(s, "ArtSkip")) html_tdb(out, p->art_skip, "right"); else
                    if (strCaseEq(s, "BytesFrom")) html_tdl(out, p->bytes_from, "right"); else
                    if (strCaseEq(s, "PGPgood")) html_tdb(out, p->pgp_good, "right"); else
                    if (strCaseEq(s, "PGPfail")) html_tdb(out, p->pgp_fail, "right"); else
                    {logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
                    if (s2)
                        *s2 = '-';
                }
            strStackAdd(out, "</tr>\n");
        }
    strStackAdd(out, ENDTABLE);
}

MAC(tasklist)
/* [<][>][^][v][top][bottom][index][help] */
{
    int tn;
    struct task_info *task;
    macro_html_table(out, argc-1, argv+1);
    for (tn=0; tn<=Stats->task_high; tn++)
        {
            int n;
            task = TaskList[tn]; /* XXX atomic locking */
            if (!task)
                continue;
            strStackAdd(out, "<tr>");
            for (n=1; n<argc; n++)
                {
                    char *s = argv[n];
                    char *s2 = strchr(s, '-');
                    if (s2)
                        *s2 = '\0';
                    if (strCaseEq(s, "Type")) html_td(out, task->ti_name, "left"); else
                    if (strCaseEq(s, "Pid")) html_tdi(out, task->ti_pid, "right"); else
                    if (strCaseEq(s, "Started")) html_tdd(out, task->ti_started, "right"); else
                    if (strCaseEq(s, "Credentials")) html_td(out, task->ti_client_host, "right"); else
                    if (strCaseEq(s, "Server")) html_td(out, task->ti_CurrentScfg? task->ti_CurrentScfg->host: NULL, "right"); else
                    if (strCaseEq(s, "Group")) html_td(out, task->ti_CurrentGroup, "left"); else
                    if (strCaseEq(s, "Arts")) html_tdi(out, task->ti_ArtRead, "right"); else
                    if (strCaseEq(s, "Groups")) html_tdi(out, task->ti_GroupsEntered, "right"); else
                    if (strCaseEq(s, "Status")) html_td(out, task->ti_status_line, "left"); else
                    {logen (("unkown parameter '%s' in @@%s@@", s, argv[0])); strStackAdd(out, "<td></td>");}
                    if (s2)
                        *s2 = '-';
                }
            strStackAdd(out, "</tr>\n");
        }    
    strStackAdd(out, ENDTABLE);
}

MAC(newsgroups)
/* [<][>][^][v][top][bottom][index][help] */
{
    struct newsgroup *n;
    char *pat;
    pat = argv[1];
    macro_html_table (out, argc-2, argv+2);
    for (n=Ni->newsgroup_head; n; n=n->next)
        {
#ifdef READ_LOCKS
            if (!newsgroupLockRead(n))
                continue;
#endif
            if (!match (pat, n->group, 1, 0))
                goto cont;
            html_newsgroup(out, argc-2, argv+2, n);
        cont:
#ifdef READ_LOCKS
            n->read_locks--;
#else
            continue;
#endif
        }
    strStackAdd(out, ENDTABLE);
}

MAC(lists)
/* [<][>][^][v][top][bottom][index][help] */
{
    int n;
    macro_html_table(out, argc-1, argv+1);
    add("<tr>\n");
    add("<th align=center>List</th>");
    add("<th align=center>Entries</th>");
    add("<th align=center>Length</th>");
    add("<th align=center>Requests</th>");
    for (n = 0; lists[n].name; n++)
        {
            struct list_stats *t = &Stats->list_stats[lists[n].type];
            add("<tr>");
            html_td(out, lists[n].name, "left");
            html_tdb(out, t->entries, "right");
            html_tdl(out, t->len, "right");
            html_tdb(out, t->cache_stats.requests, "right");
            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>            add("</tr>\n");
        }
    add(ENDTABLE);
}

MAC(cpucen=699>